﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Apewer.Web
{

    /// <summary>表示 API 行为结果，主体为流。</summary>
    public sealed class StreamResult : HeadResult, IDisposable
    {

        #region content

        /// <summary>主体。</summary>
        public Stream Stream { get; set; }

        /// <summary>Content-Length 的值，用于限制输出的最大长度。指定为 -1 时不限长度，输出到流的末尾。</summary>
        /// <value>Default = -1</value>
        public long Length { get; set; }

        /// <summary>输出后自动释放流。</summary>
        public bool AutoDispose { get; set; }

        /// <summary>创建结果实例。</summary>
        /// <param name="stream">要输出的流。</param>
        /// <param name="contentType">内容类型。</param>
        /// <param name="autoDispose">输出后自动释放流。</param>
        public StreamResult(Stream stream, string contentType = "application/octet-stream", bool autoDispose = true) : this(200, stream, contentType, -1, autoDispose) { }

        /// <summary>创建结果实例。</summary>
        /// <param name="status">HTTP 状态码。</param>
        /// <param name="stream">要输出的流。</param>
        /// <param name="contentType">内容类型。</param>
        /// <param name="length">Content-Length 的值。</param>
        /// <param name="autoDispose">输出后自动释放流。</param>
        public StreamResult(int status, Stream stream, string contentType = "application/octet-stream", long length = -1, bool autoDispose = true) : base(status)
        {
            Headers.Add("Content-Type", contentType);
            Stream = stream;
            Length = length;
            AutoDispose = autoDispose;
        }

        #endregion

        #region execute

        /// <summary>释放系统资源。</summary>
        public override void Dispose()
        {
            if (Stream != null)
            {
                RuntimeUtility.Dispose(Stream);
                Stream = null;
            }
        }

        /// <summary>写入 HTTP 头和主体。</summary>
        public override void ExecuteResult(ApiContext context)
        {
            try
            {
                if (Stream == null || Length == 0L)
                {
                    WriteHead(context, 0);
                }
                else
                {
                    WriteHead(context, Length);

                    var writed = 0L;
                    var capacity = 4096;
                    var buffer = new byte[capacity];
                    var body = context.Provider.ResponseBody();

                    // 限制长度。
                    if (Length > 0L)
                    {
                        var remains = Length;
                        while (true)
                        {
                            var limit = Math.Min((int)remains, capacity);
                            var read = Stream.Read(buffer, 0, limit);
                            if (read < 1) break;

                            body.Write(buffer, 0, read);
                            writed += read;
                            remains -= read;
                            if (remains < 1L) break;
                        }
                    }

                    // 不限制长度，输出到流的末尾。
                    else
                    {
                        while (true)
                        {
                            var read = Stream.Read(buffer, 0, capacity);
                            if (read < 1) break;

                            body.Write(buffer, 0, read);
                            writed += read;
                        }
                    }

                    context.Provider.End();
                }
            }
            finally
            {
                if (AutoDispose) Dispose();
            }
        }

        #endregion

    }

}
